﻿using EWSEditor.CRM.Forms;
using Microsoft.Xrm.Sdk;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace EWSEditor.CRM
{
    class Diagnostic
    {

        public List<DiagnosticCheck> ConfigurationChecks = new List<DiagnosticCheck>();
        public bool DiagnosticComplete;
        private Dictionary<string, string> _orgSettingList;
        public Dictionary<string, string> OrgSettingList
        {
            get { return _orgSettingList; }
        }

        public Diagnostic()
        {
        
        }

        /// <summary>
        /// Initializes all of the diagnostic checks.
        /// To help with maintainability, I've chosen to use an object initialization pattern for each check.
        /// Each Initialized DiagnosticCheck needs to be ran via RunCheck().
        /// TO-DO: Use event handler to automatically run the check once the object is initialized. 
        /// </summary>
        public void Initialize(DiagnosticForm diagForm)
        {
            UpdateCache();
            InitializeOrgSettingList();
            AddOrgDBOrgSettings(diagForm);

            DiagnosticCheck IgnoreInternalEmail = new DiagnosticCheck("Track Email Between Users", "CheckIgnoreInternalEmail", "ChangeIgnoreInternalEmail", null, DiagnosticType.Configuration, false, true, false, diagForm);
            ConfigurationChecks.Add(IgnoreInternalEmail);
            IgnoreInternalEmail.RunCheck();

            DiagnosticCheck EmailCorrelation = new DiagnosticCheck("Email Correlation", "CheckEmailCorrelation", "ChangeEmailCorrelation", null, DiagnosticType.Configuration, false, true, false, diagForm);
            ConfigurationChecks.Add(EmailCorrelation);
            EmailCorrelation.RunCheck();

            DiagnosticCheck TrackingToken = new DiagnosticCheck("Tracking Token & Smart Matching", "CheckCorrelationMethods", null, null, DiagnosticType.Configuration, false, true, false, diagForm);
            ConfigurationChecks.Add(TrackingToken);
            TrackingToken.RunCheck();

            DiagnosticCheck ConnectionChannelForACT = new DiagnosticCheck("ACT Connection Channel", "CheckConnectionChannelForACT", null, null, DiagnosticType.Configuration, false, true, false, diagForm);
            ConfigurationChecks.Add(ConnectionChannelForACT);
            ConnectionChannelForACT.RunCheck();

            DiagnosticCheck AutoPromoteSetting = new DiagnosticCheck("Automatic Promotion Setting", "CheckAutoPromote", null, null, DiagnosticType.IncomingEmail, false, true, false, diagForm);

            //Do not look for autopromote setting if we are signed in as Support User and TargetMailboxIsQueue
            if ((CrmOrgMailboxCache.TargetMailboxIsQueue) && (!CrmOrgMailboxCache.SupportMode))
            {
                ConfigurationChecks.Add(AutoPromoteSetting);
                AutoPromoteSetting.RunCheck();
            }

            else if(!CrmOrgMailboxCache.TargetMailboxIsQueue)
            {
                ConfigurationChecks.Add(AutoPromoteSetting);
                AutoPromoteSetting.RunCheck();

                DiagnosticCheck AutoCreateContact = new DiagnosticCheck("Auto Create Contacts", "CheckAutoCreateContacts", null, null, DiagnosticType.IncomingEmail, false, true, false, diagForm);
                ConfigurationChecks.Add(AutoCreateContact);
                AutoCreateContact.RunCheck();

                if (!CrmOrgMailboxCache.SupportMode)
                {
                    DiagnosticCheck CheckForSyncFilters = new DiagnosticCheck("Sync Filter Status", "CheckSyncFilters", null, "https://www.microsoft.com/en-US/dynamics/crm-customer-center/create-or-modify-synchronization-filters-or-view-synchronizing-fields.aspx", DiagnosticType.ACT, false, false, true, diagForm);
                    ConfigurationChecks.Add(CheckForSyncFilters);
                    CheckForSyncFilters.RunCheck();
                }

            }

            DiagnosticCheck IncomingEmailApproval = new DiagnosticCheck("Incoming Email Approved", "CheckEmailApproval", null, "https://technet.microsoft.com/en-us/library/mt622056.aspx#BKMK_ApproveEmail", DiagnosticType.IncomingEmail, false, true, true, diagForm);
            ConfigurationChecks.Add(IncomingEmailApproval);
            IncomingEmailApproval.RunCheck();

            if (((OptionSetValue)CrmOrgMailboxCache.Organization["emailconnectionchannel"]).Value == 0)
            {
                DiagnosticCheck IncomingEmailStatus = new DiagnosticCheck("Incoming Email Status", "CheckIncomingEmailStatus", null, null, DiagnosticType.IncomingEmail, false, true, false, diagForm);
                ConfigurationChecks.Add(IncomingEmailStatus);
                IncomingEmailStatus.RunCheck();

                DiagnosticCheck ACTStatus = new DiagnosticCheck("ACT Status", "CheckACTStatus", null, null, DiagnosticType.ACT, false, true, false, diagForm);
                ConfigurationChecks.Add(ACTStatus);
                ACTStatus.RunCheck();
            }

            if (((OptionSetValue)CrmOrgMailboxCache.TargetMailbox["incomingemailstatus"]).Value == 1)
            {
                DiagnosticCheck ReceivingPostPonedUntilLatency = new DiagnosticCheck("Latency in Email Processing", "CheckReceivingPostPonedUntil", null, null, DiagnosticType.IncomingEmail, false, true, false, diagForm);
                ConfigurationChecks.Add(ReceivingPostPonedUntilLatency);
                ReceivingPostPonedUntilLatency.RunCheck();
            }

            if (((OptionSetValue)CrmOrgMailboxCache.TargetMailbox["actstatus"]).Value == 1)
            {
                DiagnosticCheck ReceivingPostPonedUntilForACTLatency = new DiagnosticCheck("Latency in ACT Processing", "CheckReceivingPostPonedUntilForACT", null, null, DiagnosticType.ACT, false, true, false, diagForm);
                ConfigurationChecks.Add(ReceivingPostPonedUntilForACTLatency);
                ReceivingPostPonedUntilForACTLatency.RunCheck();
            }



            //Called after all DiagnosticChecks have been initialized
            CheckForCompletedChecks();
        }

        private void InitializeOrgSettingList()
        {
            _orgSettingList = new Dictionary<string, string>();
            _orgSettingList.Add("AllowPromoteDuplicates", "Boolean");
            _orgSettingList.Add("AutoCreateContactOnPromote", "Boolean");
            _orgSettingList.Add("DisableSmartMatching", "Boolean");
            _orgSettingList.Add("DoNotIgnoreInternalEmailToQueues", "Boolean");
            _orgSettingList.Add("EnableAppointmentBroadcastingForOutlookSync", "Integer");
            _orgSettingList.Add("EnableReLinkingToExistingCRMRecord", "Integer");
            _orgSettingList.Add("OutlookSyncDataSubscriptionClientsBatchSize", "Integer");

        }

        private void AddOrgDBOrgSettings(DiagnosticForm diagForm)
        {
            if(CrmOrgMailboxCache.OrgDBOrgSettings.Keys.Count > 0)
            {
                foreach(System.Collections.Generic.KeyValuePair<string, string> keypair in CrmOrgMailboxCache.OrgDBOrgSettings)
                {
                    if(_orgSettingList.ContainsKey(keypair.Key.ToString()))
                    {
                        CreateDiagnosticCheckForSetting(keypair, diagForm);

                    }
                }
            }
            else
            {
                return;
            }
        }

        private void CreateDiagnosticCheckForSetting(KeyValuePair<string, string> keypair, DiagnosticForm diagForm)
        {
            DiagnosticCheck OrgDBOrgCheck = new DiagnosticCheck(keypair.Key.ToString(), "CheckOrgDbOrgSetting", null, null, DiagnosticType.OrgDbOrgSetting, true, true, true, diagForm);
            ConfigurationChecks.Add(OrgDBOrgCheck);
            OrgDBOrgCheck.RunCheck();
        }

        private void UpdateCache()
        {
            CrmOrgMailboxCache.RefreshCache();
        }

        /// <summary>
        /// Recursively scans the Checks collection every second to see if all tests have finished.
        /// This causes Initialize() to stall until all background workers have completed their tests.
        /// </summary>
        private void CheckForCompletedChecks()
        {
            bool KeepChecking = true;

            while (KeepChecking)
            {

                foreach (DiagnosticCheck diagCheck in ConfigurationChecks)
                {
                    if (!diagCheck.TestFinished)
                    {
                        Thread.Sleep(1000);
                        break;
                    }
                    else if (ConfigurationChecks.Last<DiagnosticCheck>() == diagCheck)
                    {
                        KeepChecking = false;
                    }
                }
            }
        }
    }
}
